package ca.kooki.codesnippet;

import java.util.ArrayList;
import java.util.List;

import ca.kooki.codesnippet.core.Action;
import ca.kooki.codesnippet.core.ActionResponse;
import ca.kooki.codesnippet.core.SearchInput;
import ca.kooki.codesnippet.core.User;

public class InMemoryCodeSnippetRepository implements CodeSnippetRepository {

  //```````````````````````
  // STATIC VARIABLES
  //```````````````````````

  private static final InMemoryCodeSnippetRepository singleton = new InMemoryCodeSnippetRepository();
  
  //```````````````````````
  // MEMBER VARIABLES
  //```````````````````````

  private CodeSnippetRepositoryHelper _helper;
  private List<CodeSnippet> _allSnippets;
  private List<User> _allUsers;
  private List<Tag> _allTags;
  private List<SearchInput> _allSearches;
  private List<Action> _allActions;
  
  private int _nextCodeSnippetId;
  private int _nextUserId;
  private int _nextTagId;
  
  //```````````````````````
  // CONSTRUCTOR
  //```````````````````````

  public InMemoryCodeSnippetRepository()
  {
    init();
  }
  
  private void init()
  {
    _helper = new CodeSnippetRepositoryHelper();
    _allSnippets = new ArrayList<CodeSnippet>();
    _allUsers = new ArrayList<User>();
    _allTags = new ArrayList<Tag>();
    _allSearches = new ArrayList<SearchInput>();
    _allActions = new ArrayList<Action>();
    _nextCodeSnippetId = 1;
    _nextUserId = 1;
    _nextTagId = 1;
  }
  
  public static CodeSnippetRepository getDefault() {
    return singleton;
  }

  public static void resetDefault()
  {
    singleton.init();
  }
  
  //```````````````````````
  // INTERFACE
  //```````````````````````
  
  public User getUser()
  {
    return _helper.getUser();
  }
  
  public void setUser(User user)
  {
    _helper.setUser(user);
  }
  
  public void setUser(String username)
  {
    setUser(getUserByUsername(username));
  }
  
  public ActionResponse addCodeSnippet(CodeSnippet snippet) 
  {
    ActionResponse response = new ActionResponse();
    Action action = _helper.actionForNewCodeSnippet(snippet);
    
    if (snippet.getCode().trim().equals(""))
    {
      response.addError("no_code_snippet");
      return addAction(response, action);
    }
    
    for (int i=0; i<_allSnippets.size(); i++)
    {
      CodeSnippet existingSnippet = _allSnippets.get(i);
      if (existingSnippet.isSameCodeAs(snippet))
      {
        snippet.setId(existingSnippet.getId());
        return response;
      }
    }
    
    snippet.setId(_nextCodeSnippetId + "");
    _nextCodeSnippetId++;
    _allSnippets.add(snippet);
    return addAction(response, action);
  }
  
  public ActionResponse addUser(User user)
  {
    ActionResponse response = new ActionResponse();
    
    Action action = _helper.actionForNewUser(user);
    
    if (user.getName().trim().equals(""))
    {
      response.addError("empty_user_name");
      return addAction(response,action);
    }

    for(int i=0; i<_allUsers.size(); i++)
    {
      User existing = (User)_allUsers.get(i);
      if (existing.getName().equals(user.getName()))
      {
        response.addError("existing_user_name");
        return addAction(response,action);
      }
    }
    
    user.setId(_nextUserId + "");
    _nextUserId++;
    _allUsers.add(user);
    
    return addAction(response,action);
  }

  public ActionResponse addTag(Tag tag)
  {
    
    ActionResponse response = new ActionResponse();
    Action action = _helper.actionForNewTag(tag);
    
    if (tag.getUser().getName() == "")
    {
      response.addError("no_user_for_tag");
      return addAction(response,action);
    }
    addUser(tag.getUser());

    response.merge(addCodeSnippet(tag.getCodeSnippet()));
    
    if (!response.isValid())
    {
      return addAction(response,action);
    }
    
    if (tag.isOfficial())
    {
      for (int i=0; i<_allTags.size(); i++)
      {
        Tag oldTag = _allTags.get(i);
        if (oldTag.getId().equals(tag.getId()))
        {
          _allTags.set(i, tag);
          break;
        }
      }
    }
    else
    {
      tag.setId(_nextTagId + "");
      _nextTagId++;
      _allTags.add(tag);
    }
    
    return addAction(response,action);
  }


  public CodeSnippet[] getAllSnippets() 
  {
    return (CodeSnippet[])_allSnippets.toArray(new CodeSnippet[_allSnippets.size()]);
  }

  public User getUserByUsername(String username)
  {
    if (username == null)
    {
      username = "";
    }

    for (int i=0; i<_allUsers.size(); i++)
    {
      User potentialMatch = (User)_allUsers.get(i);
      if (potentialMatch.getName().equals(username))
      {
        return potentialMatch;
      }
    }
    return new User(username);
  }
  
  public User[] getAllUsers()
  {
    return (User[])_allUsers.toArray(new User[_allUsers.size()]);
  }
  
  public Tag[] getAllTags(String searchQuery)
  {
    List<String> allCodeSnippetsToDate = new ArrayList<String>();
    Tag[] allTags = (Tag[])_allTags.toArray(new Tag[_allTags.size()]);

    
    List<Tag> allRelevantTags = new ArrayList<Tag>();
    for (int i=0; i<allTags.length; i++)
    {
      Tag potentialTag = allTags[i];
      String codeSnippetId = potentialTag.getCodeSnippet().getId();
      
      if (potentialTag.isRelevant(searchQuery) && !allCodeSnippetsToDate.contains(codeSnippetId))
      {
        allRelevantTags.add(potentialTag);
        allCodeSnippetsToDate.add(codeSnippetId);
      }
    }

    if (searchQuery != null)
    {
      _allSearches.add(new SearchInput(getUser(),searchQuery));
      Action action = _helper.actionForNewSeach(searchQuery,allRelevantTags.size());
      addAction(new ActionResponse(), action);
    }    
    
    return (Tag[])allRelevantTags.toArray(new Tag[allRelevantTags.size()]);
  }
  
  public SearchInput[] getAllSearches(String searchQuery)
  {
    if (searchQuery == null || searchQuery == "")
    {
      return (SearchInput[])_allSearches.toArray(new SearchInput[_allSearches.size()]);
    }
    
    return new SearchInput[0];
  }
  
  public Action[] getAllActions()
  {
    return (Action[])_allActions.toArray(new Action[_allActions.size()]);
  }
  
  
  public boolean isUser(String username)
  {
    User[] allUsers = getAllUsers();
    for (int i=0; i<allUsers.length; i++)
    {
      User potentialMatch = allUsers[i];
      if (potentialMatch.getName().equals(username))
      {
        return true;
      }
    }
    return false;
  }
  
  public StatisticSummary getStatistics()
  {
    StatisticSummary summary = new StatisticSummary();
    for (int i=0; i<_allTags.size(); i++)
    {
      summary.addTag(_allTags.get(i));
    }
    return summary;
  }
  
  public ActionResponse isValid()
  {
    ActionResponse response = new ActionResponse();
    response.setSuccessMessage("repository_configured_properly");
    return response;
  }
  
  //```````````````````````
  // PRIVATE METHODS
  //```````````````````````
  
  private ActionResponse addAction(ActionResponse response, Action action)
  {
    action.setSuccess(response.isValid());
    _allActions.add(action);
    return response;
  }


}
